home *** CD-ROM | disk | FTP | other *** search
- /*
- Windows95 MDEF Emulator
- Version 2.0
-
- Evolved from the NeXT MDEF
- written on Jan. 17, 1994 (Right after the Northridge earthquake—whew!!)
-
- by Hiep Dam, From The Witches' Brew
- Contact: America Online -> StarLabs
- Internet -> starlabs@aol.com
-
-
- FREE! FREE! FREE!
- This code & MDEF are in the public domain.
-
-
- Usage:
-
- Default resource id of MDEF: 1972
- Go into ResEdit and edit the menu's MDEF ID to 1972. That's it!
-
- If you just use the MDEF by itself, by default the menus will have
- a nice 3D grayish look. Your average 3D color customized look...
- Nice, but boring to some.
-
- ...
-
- However, things get more exciting if you add custom resources.
- The MDEF looks for any resources of type 'MnuT' ("Menu template")
- that are the same resource id as the menu. If it doesn't find any
- then it looks for one with the default id of 0. Barring that it
- uses the default look mentioned above.
-
- The 'MnuT' resources define how the Windows95 MDEF colors and draws
- the menu. You specify 3 RGB colors: the menu background color, the
- menu hilite (light) color, and the menu shadow (dark) color. The
- shadow color will also be used to hilite the selected menu item for
- that menu. You must also specify in the 'MnuT' resource the menu's
- font, font size, and font face.
-
- The use of 'MnuT' makes the Windows95 MDEF very versatile and gives
- you control over the color scheme and font appearance of your
- menus. Note: 'mctb' resources are not supported; they're
- really too overblown for my tastes.
-
-
- Notes:
-
- What this MDEF supports:
- * Menu items as submenu items (i.e. points to a submenu w/ a triangle)
- * as a submenu itself
- * cmd-keys
- * item marks
- * item text styles
-
- What this MDEF does not support:
- * meta-characters and parsing menu item text for such characters
- * icons, sicns, reduced icons, cicns. In other words, no icons.
- * scrolling. If you have a lot of menu items, the ones near the
- bottom of the screen will be clipped. Don't use too many items!
-
-
-
- 1) This MDEF makes several IMPORTANT *assumptions* about the environment
- it's running on. Running in an incorrect environment will result
- in messy crashes.
- a) Color Quickdraw is present. It uses RGBColors to draw the menu,
- not the old-style 8-color Quickdraw model. There is no check
- to see if Color Quickdraw is available or not. If the MDEF is
- running in a b&w environment, there will be an "unimplemented
- trap" error. The MDEF absolutely requires Color QD.
- b) System 7 is present.
-
- 2) The original portions of the NeXT MDEF were derived from an example MDEF
- included in THINK Reference. See "Custom Menus" in the Menu Manager
- section. The code has changed significantly with the move over to
- a Windows95 look from the NeXT look.
-
- 3) I think this code is a good place to start when writing your own
- customized MDEFs. Note that to write MDEFs which support more than just
- the most rudimentary stuff (i.e. more than plain text) things can get
- hairy pretty fast. You have to take into account submenus, item
- marks, command keys, large icons, small icons, and so on. And if
- you want to support extensions such as other modifier keys like
- control, shift, and option keys; whew, it's a lot of work. I
- don't envy the author of the Mercutio MDEF...
-
-
- Version History:
- 1.0: Initial release (limited to a source code CD-ROM); was NeXT MDEF
-
- 2.0: 11/28/95
- Modified mdef -> now Windows95 MDEF. Boxy menu item look removed
- (admittedly it had looked rather tacky).
- Pretty much complete overhaul. Gotta say it: my old code was
- pretty messy...
-
- 2.0.1: 12/1/95
- Fixed submenu problem partially by using mbSaveLoc. What a
- pain! I have to muck around low-memory system globals JUST
- TO MAKE THE DARN THING WORK! Most of the stuff can be found
- in the mChooseMsg handler.
- Also "fixed" popup menu problem using a sample MDEF written
- by Ken Worley. Thanks Ken! (The popup menu still might pop
- up incorrectly if the system 7 popup menu cdef is used and
- the menu is placed near the edges of the monitor. I figure
- it's the system's fault - not mine). Stuff can be found in
- the mPopUpMsg handler.
-
- */
-
- // ---------------------------------------------------------------------------
-
- #include <Types.h>
- #include <Memory.h>
- #include <Quickdraw.h>
- #include <Fonts.h>
- #include <ToolUtils.h>
- #include <Icons.h>
- #include <Controls.h>
- #include <Gestalt.h>
- #include <Resources.h>
-
- #include "DrawMenuItem.h"
- #include "GetDominantDevice.h"
-
- // ---------------------------------------------------------------------------
-
- /*
- Writing an MDEF which properly uses submenus (among other things)
- requires that we mess with undocumented system globals, a practice
- that will probably blow up in our faces in the future. Set to
- 0 when that time comes, recompile, and voila! Problems should then
- be fixed if we have written the MDEF well.
- */
-
- #define USE_UNDOCUMENTED_STUFF 1
-
- // ---------------------------------------------------------------------------
-
- // Some prototypes
- pascal void main(short msg, MenuHandle whichMenu, Rect *menuRect,
- Point hitPt, short *itemID);
-
-
- static void FillInColor(
- RGBColor *theColor,
- unsigned short r,
- unsigned short g,
- unsigned short b);
- static void InitMenuData(short menuID, MDEFstuff *data);
-
- static short GetMenuItemHeight();
- static short GetMenuHeight(short numItems);
- static short FindMenuItem(MenuHandle whichMenu, Rect *menuRect, Point hitPt);
- static short GetMenuWidth(MenuHandle whichMenu, short numItems, MDEFstuff *mdefData);
-
- static void DoSizeMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData);
- static void DoPopUpMsg(
- MenuHandle whichMenu,
- Rect *menuRect,
- short menuItem,
- Point hitPt,
- MDEFstuff *mdefData);
- static void DoDrawMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData);
- static void DoChooseMsg(
- MenuHandle whichMenu,
- Rect *menuRect,
- Point hitPt,
- short *itemID,
- MDEFstuff *mdefData);
-
-
- // ----------------------------------------------------------------------
-
- /*
- Main.
-
- This is the entrypoint for the MDEF. So what does that mean? Simply
- this is the function that will be called when the MDEF is used.
- It checks what is the current message sent to it, case goes
- thru a switch statement to find the correct handler for the msg.
- */
-
- pascal void main(short msg, MenuHandle whichMenu, Rect *menuRect, Point hitPt,
- short *theMenuItem) {
- MDEFstuff menuData;
- GrafPtr curPort;
- short saveFont, saveSize, saveFace, saveMode;
- RGBColor saveFore, saveBack;
- PenState savePen;
-
- // We're implicitly drawing into the Window Manager port, so make
- // sure we save the important settings so we can restore them later on.
- GetPort(&curPort);
- saveFont = curPort->txFont;
- saveSize = curPort->txSize;
- saveFace = curPort->txFace;
- saveMode = curPort->txMode;
- GetForeColor(&saveFore);
- GetBackColor(&saveBack);
- GetPenState(&savePen);
-
- InitMenuData((**whichMenu).menuID, &menuData);
- TextFont(menuData.params.menuFont);
- TextSize(menuData.params.menuSize);
- TextFace(menuData.params.menuFace);
-
- switch (msg) {
- case mDrawMsg: {
- DoDrawMsg(whichMenu, menuRect, &menuData);
- } break;
-
- case mChooseMsg: {
- DoChooseMsg(whichMenu, menuRect, hitPt, theMenuItem, &menuData);
- } break;
-
- case mSizeMsg: {
- DoSizeMsg(whichMenu, menuRect, &menuData);
- } break;
-
- case mPopUpMsg: {
- DoPopUpMsg(whichMenu, menuRect, *theMenuItem, hitPt, &menuData);
- } break;
- } // END switch
-
- // Polite manners: if we changed the font, restore the system font
- // upon exiting...
- TextFont(saveFont);
- TextSize(saveSize);
- TextFace(saveFace);
- TextMode(saveMode);
- RGBForeColor(&saveFore);
- RGBBackColor(&saveBack);
- SetPenState(&savePen);
- } // END main
-
- // ----------------------------------------------------------------------
-
- /*
- IsItemDisabled.
-
- Finds out whether the given menu item is disabled or not. Note though
- that we also have to check if the *entire* menu is disabled or not, in
- addition to the menu item. We do this by checking bit 0 of the
- enableFlags field in the menuInfo structure of a menu.
- This is done because the menu can be disabled regardless whether
- the menu item is enabled or not - the menu takes precedence over
- the menu items.
-
- With the advent of Copland, this part will be incompatible. Hopefully
- accessors for a menu item's enabled/disabled state will be available.
- */
-
- Boolean IsItemDisabled(MenuHandle whichMenu, short whichItem) {
- if (whichItem > 31)
- return(false); // Items > 31 always enabled
-
- Str255 itemText;
- GetItem(whichMenu, whichItem, itemText);
- if (itemText[1] == kDividerChar)
- return(true); // Divider menu items always disabled.
-
- return(!BitTst(&(**whichMenu).enableFlags, 31 - whichItem) ||
- !BitTst(&(**whichMenu).enableFlags, 31 - 0));
- } // END IsItemDisabled
-
- // ----------------------------------------------------------------------
-
- /*
- GetMenuItemHeight.
-
- Get the height of any single menu item. If you wish to add icons
- to your menu, you'll have to change this to accomodate larger icons.
- This simply calls _GetFontInfo.
- */
-
- short GetMenuItemHeight() {
- FontInfo theInfo;
- GetFontInfo(&theInfo);
- // Account for padding for both top and bottom (that's why we're *2)
- return(theInfo.ascent + (kHeightPadding + kHeightPadding));
- } // END GetMenuItemHeight
-
- // ----------------------------------------------------------------------
-
- /*
- GetMenuHeight.
-
- Gets the height of a single menu item, and finds the height
- of the whole menu by multiplying a single menu item height
- by the number of menu items.
- */
-
- short GetMenuHeight(short numItems) {
- return((numItems * GetMenuItemHeight()) + (kRectPadding + kRectPadding));
- } // END GetMenuHeight
-
- // ----------------------------------------------------------------------
-
- /*
- GetMenuWidth.
-
- Gets the width of the entire menu, by finding the width of the
- widest menu item in the menu. Polls each menu item for its width,
- keeping track of the largest width.
-
- Accounts for submenu "triangles" mini-icon and cmd-keys in menu
- item width.
- */
-
- short GetMenuWidth(MenuHandle whichMenu, short numItems, MDEFstuff *mdefData) {
- Str255 itemText;
- short maxLength = 0;
- short curLength = 0;
- short theChar;
- short cmdWidth;
- short checkWidth;
- Style style;
- Boolean hasCmdKeys = false;
-
- checkWidth = CharWidth(kCheckMarkChar) +
- kItemMarkPadding + kItemMarkPadding; // 4 = left & right padding
- cmdWidth = CharWidth(kCmdKeyChar); // "Cmd" key clover character
- cmdWidth += CharWidth(kWidestChar); // "W" is widest character in the bunch
-
- for (short i = 1; i <= numItems; i++) {
- GetItem(whichMenu, i, itemText);
- GetItemStyle(whichMenu, i, &style);
- if (style)
- TextFace(style);
- else
- TextFace((mdefData->params).menuFace);
- curLength = StringWidth(itemText);
-
- GetItemCmd(whichMenu, i, &theChar);
- if (theChar != 0)
- hasCmdKeys = true;
-
- curLength += checkWidth;
-
- if (curLength > maxLength)
- maxLength = curLength;
- }
-
- // Padding is both to left and right of cmdKey+char itself.
- if (hasCmdKeys)
- maxLength += (cmdWidth + (kCmdKeyPadding + kCmdKeyPadding));
-
- return(maxLength + (kWidthPadding + kWidthPadding));
- } // END GetMenuWidth
-
- // ----------------------------------------------------------------------
-
- /*
- GetMenuItemRect.
- Determine rect of menu item. Pretty obvious.
- */
-
- void GetMenuItemRect(Rect *menuRect, Rect *itemRect, short whichItem) {
- short oneHeight = GetMenuItemHeight();
-
- //InsetRect(menuRect, kRectPadding, kRectPadding);
- itemRect->left = menuRect->left;
- itemRect->right = menuRect->right;
- itemRect->top = menuRect->top + (oneHeight * (whichItem - 1));
- itemRect->bottom = itemRect->top + oneHeight;
- //InsetRect(menuRect, -kRectPadding, -kRectPadding);
- } // END GetMenuItemRect
-
- // ---------------------------------------------------------------------------
-
- // FindMenuItem.
- // Given a point, find if this point is within the rect of any menu
- // item. If so, return the item, else 0.
-
- short FindMenuItem(MenuHandle whichMenu, Rect *menuRect, Point hitPt) {
- Rect itemRect;
- short itemCount = CountMItems(whichMenu);
-
- if (!PtInRect(hitPt, menuRect))
- return(0);
-
- for (short i = 1; i <= itemCount; i++) {
- GetMenuItemRect(menuRect, &itemRect, i);
- if (PtInRect(hitPt, &itemRect)) {
- return(i);
- }
- }
- return(0);
- } // END FindMenuItem
-
- // ---------------------------------------------------------------------------
- // ---------------------------------------------------------------------------
-
- /*
- DoSizeMsg.
- Even more obvious (más o menos). Calls others to do the job.
- */
-
- void DoSizeMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData) {
- short itemCount = CountMItems(whichMenu);
-
- (**whichMenu).menuWidth = GetMenuWidth(whichMenu, itemCount, mdefData);
- (**whichMenu).menuHeight = GetMenuHeight(itemCount);
- } // END DoSizeMsg
-
- // ----------------------------------------------------------------------
-
- enum {
- kPopupMargin = 4
- };
-
- void DoPopUpMsg(
- MenuHandle whichMenu,
- Rect *menuRect,
- short menuItem,
- Point hitPt,
- MDEFstuff *mdefData) {
- /*
- Note how hitPt.h and hitPt.v are matched to top and left.
- Not intuitive! Another quirk in the OS.
- */
-
- short itemCount = CountMItems(whichMenu);
- menuRect->top = hitPt.h - (menuItem * GetMenuItemHeight());
- menuRect->left = hitPt.v;
- // Get the width & height
- DoSizeMsg(whichMenu, menuRect, mdefData);
- menuRect->right = menuRect->left + (**whichMenu).menuWidth;
- menuRect->bottom = menuRect->top + (**whichMenu).menuHeight;
-
- Rect deviceBounds;
- deviceBounds = (**GetDominantDevice(menuRect)).gdRect;
- if (menuRect->bottom > (deviceBounds.bottom-kPopupMargin))
- OffsetRect(menuRect, 0, (deviceBounds.bottom-kPopupMargin) - menuRect->bottom);
-
- if (menuRect->right > (deviceBounds.right-kPopupMargin))
- OffsetRect(menuRect, (deviceBounds.right-kPopupMargin) - menuRect->right, 0);
-
- if (menuRect->top < (deviceBounds.top+kPopupMargin))
- OffsetRect(menuRect, 0, (deviceBounds.top+kPopupMargin) - menuRect->top);
-
- if (menuRect->left < (deviceBounds.left+kPopupMargin))
- OffsetRect(menuRect, (deviceBounds.left+kPopupMargin) - menuRect->left, 0);
- } // END DoPopUpMsg
-
- // ---------------------------------------------------------------------------
-
- /*
- DoDrawMsg.
- Erases the entire menu, and calls each menu item individually to
- draw itself.
- */
-
- void DoDrawMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData) {
- short itemCount = CountMItems(whichMenu);
-
- RGBBackColor(&(mdefData->params).menuBkgndColor);
- EraseRect(menuRect);
-
- PenSize(1, 1);
- InsetRect(menuRect, 1, 1);
- RGBForeColor(&(mdefData->params).menuHiliteColor);
- MoveTo(menuRect->left, menuRect->top);
- LineTo(menuRect->right, menuRect->top);
- MoveTo(menuRect->left, menuRect->top);
- LineTo(menuRect->left, menuRect->bottom);
- RGBForeColor(&(mdefData->params).menuShadowColor);
- MoveTo(menuRect->right, menuRect->bottom);
- LineTo(menuRect->right, menuRect->top);
- MoveTo(menuRect->right, menuRect->bottom);
- LineTo(menuRect->left, menuRect->bottom);
-
- for (short i = 1; i <= itemCount; i++) {
- DrawMenuItem(whichMenu, menuRect, i, kMenuUnhilited, mdefData);
- }
- InsetRect(menuRect, -1, -1);
- } // END DoDrawMsg
-
- // ----------------------------------------------------------------------
-
- #pragma mark UNDOCUMENTED DATA!
-
- #if USE_UNDOCUMENTED_STUFF
- /*
- Define some undocumented low-memory system globals. You can bet
- yer dollar this will break in future releases of the MacOS, but
- doing this is a necessary evil, as it is required to make the
- MDEF work __properly__ with submenus.
- */
-
- // mbSaveLoc, mbItemRect, and mbUglyScroll stuff
- typedef struct {
- short lastMBSave;
- long mbCustomStorage;
- Rect mbItemRect; // Greatest item of interest to us currently
- char mbMenuDelay;
- char mbMenuDrag;
- short mbUglyScroll; // Of semi-interest (tho what it does I don't know)
- short mbIconState;
- long mbHeader; // Actually, size is unknown!
- } MBGlobalData;
-
- #define mbItemRectOffset 6
- #define mbUglyScrollOffset 16
-
- #define sgMBSaveLoc *((Handle*)0x0B5C)
- #define sgMBItemRectPtr ((Rect*)((*sgMBSaveLoc) + mbItemRectOffset))
- #define sgMBUglyScrollPtr ((short*)((*sgMBSaveLoc) + mbUglyScrollOffset))
- #define sgHuhRectPtr ((Rect*)0x09FA)
-
- // menuDisable stuff
- typedef struct {
- short hiWord;
- short loWord;
- } StuffedLong;
-
- #define sgMenuDisablePtr ((StuffedLong*)0x0B54)
-
- #endif // USE_UNDOCUMENTED_STUFF
-
- // ---------------------------------------------------------------------------
-
- /*
- DoChooseMsg.
- This is the only other function which calls DrawMenuItem, other than
- DoDrawMsg. Takes care of the hiliting/unhiliting of menu items.
- */
-
- void DoChooseMsg(
- MenuHandle whichMenu,
- Rect *menuRect,
- Point hitPt,
- short *itemID,
- MDEFstuff *mdefData) {
-
- Str255 itemText;
- short mouseItem;
-
- mouseItem = FindMenuItem(whichMenu, menuRect, hitPt);
-
- if (mouseItem != 0) {
- // Pre-fetch some data we'll need later on (item text)
- GetItem(whichMenu, mouseItem, itemText);
-
- #if USE_UNDOCUMENTED_STUFF
- /*
- Boo-hoo-hoo! We need to set the mbItemRect stored in mbSaveLoc.
-
- All UNDOCUMENTED, but REQUIRED if you wish to make your mdef
- work properly with submenus. Thank God for Usenet and sample
- source code! If this is taken out, the submenus flash about
- 5 times, cause they get the mDrawMsg 5 times for some reason!
- Thus they erase, redraw 5 times, causing significant flicker.
-
- Yuck.
- */
- Rect itemRect;
- GetMenuItemRect(menuRect, &itemRect, mouseItem);
- *sgMBItemRectPtr = itemRect;
-
- /*
- Either using the below lines or commenting them out doesn't
- seem to make any difference so far. So the less we muck
- with the system the safer we'll be. Uncomment when the
- purpose(s) are found...
- */
- // *sgMBUglyScrollPtr = true;
- // *sgHuhRectPtr = itemRect; // MenuSelect seems to be needing this
- #endif // USE_UNDOCUMENTED_STUFF
- }
-
- InsetRect(menuRect, 1, 1);
- if (mouseItem == 0 || // out of bounds or disabled
- IsItemDisabled(whichMenu, mouseItem)) {
-
- DrawMenuItem(whichMenu, menuRect, *itemID, kMenuUnhilited, mdefData);
- *itemID = 0; // return "cancel" code
-
- }
- else if (mouseItem != *itemID) {
- // unhilight previous
- DrawMenuItem(whichMenu, menuRect, *itemID, kMenuUnhilited, mdefData);
- // hilight new
- DrawMenuItem(whichMenu, menuRect, mouseItem, kMenuHilited, mdefData);
- // return new
- *itemID = mouseItem;
-
- }
-
- #if USE_UNDOCUMENTED_STUFF
- // Set the MenuDisable low-mem global; whether the
- // item is disabled or not doesn't matter.
- sgMenuDisablePtr->hiWord = (**whichMenu).menuID;
- sgMenuDisablePtr->loWord = mouseItem;
- #endif // USE_UNDOCUMENTED_STUFF
-
- InsetRect(menuRect, -1, -1);
- } // END DoChooseMsg
-
- // ----------------------------------------------------------------------
-
- void InitMenuData(short menuID, MDEFstuff *data) {
- MDEFstuff **menuParameters;
-
- FillInColor(&data->white, kWhite, kWhite, kWhite);
- FillInColor(&data->black, kBlack, kBlack, kBlack);
-
- // If an optional 'MnuT' resource exists, which describes the
- // menu color and font usage, load that. Else fill in default
- // values for MDEF.
- menuParameters = (MDEFstuff**)GetResource(kDefaultMDEFparametersType,
- menuID);
- if (menuParameters == NULL) {
- // Can't find an 'MnuT' with same id as menu. Try default 'MnuT' rsrc id...
- menuParameters = (MDEFstuff**)GetResource(
- kDefaultMDEFparametersType, kDefaultMDEFparametersID);
- }
-
- if (menuParameters == NULL) {
- (data->params).menuHiliteColor = data->white;
- FillInColor(&(data->params).menuBkgndColor, kLtGray, kLtGray, kLtGray);
- FillInColor(&(data->params).menuShadowColor, kDkGray, kDkGray, kDkGray);
- (data->params).menuSelectionColor = (data->params).menuShadowColor;
- (data->params).menuFont = kMenuFont;
- (data->params).menuSize = kMenuSize;
- (data->params).menuFace = kMenuFace;
- (data->params).exactWin95Look = false;
- }
- else {
- HLock((Handle)menuParameters);
- BlockMove(*menuParameters, &data->params, sizeof(MDEFparameters));
- HUnlock((Handle)menuParameters);
- ReleaseResource((Handle)menuParameters);
- }
- } // END InitMenuData
-
- // ---------------------------------------------------------------------------
-
- /*
- FillInColor.
- If you use RGBColors, no doubt you'll be using some function similar to this.
- Now why didn't Apple include a function like this? Less overhead, I guess.
-
- Apple should also have included get/set routines for a grafport's txFont,
- txSize, txFace, and txMode. But who's perfect?
- */
-
- void FillInColor(
- RGBColor *theColor,
- unsigned short r,
- unsigned short g,
- unsigned short b) {
-
- theColor->red = r;
- theColor->green = g;
- theColor->blue = b;
- } // END FillInColor
-
- // ---------------------------------------------------------------------------
- // ---------------------------------------------------------------------------
-
- // END Windows95 MDEF.cpp